<?php
/**
 * 容器类
 *
 * User: liuqingwei
 * Date: 2020/4/2
 * Time: 下午5:14
 */

namespace library\container;

use wei\core\DB;

class Container extends ContainerAccess implements ContainerInterface
{
    protected $inject   = [];//存放给value注入的方法
    protected $instance = [];//对象存储的数组
    public function get( $key )
    {
        return $this->offsetGet($key);
    }
    
    public function set($key,$value)
    {
        $this->offsetSet($key,$value);
    }
    
    public function has($key)
    {
        return $this->offsetExists($key);
    }
    
    
    /**
     * 给容器的某个key注入方法
     * @param string $key         key
     * @param string $method      方法名称
     * @param mixed  $method_body 方法体
     * @return mixed
     */
    public function inject($key,$method,$method_body)
    {
        if (!isset($this->inject[$key][$method]))
        {
            $this->inject[$key][$method] = $method_body;
        }
    }
    
    /**
     * 获取某个key上的方法
     *
     * @param string $key         key
     * @param string $method      方法名称
     *
     * @return mixed
     */
    public function getInjectMethod($key,$method)
    {
        if (isset($this->inject[$key][$method]))
        {
            return $this->inject[$key][$method];
        }
    }
    
    /**
     * 回调函数
     * @param string $key         key
     * @param string $method      方法名称
     * @param array  $param       参数数组
     * @return mixed
     * @throws
     */
    public function callback($key,$method,$param=[])
    {
        $methods = get_class_methods($this->get($key));
        if (in_array($method,$methods,true))
        {
            call_user_func_array([$this->get($key),$method],$param);
        }
        else
        {
            $inject_method = $this->getInjectMethod($key,$method);
            if (isset($inject_method))
            {
                call_user_func_array($inject_method,$param);
            }
            else
            {
                throw new \Exception(sprintf("%s上不存在%s方法",$key,$method));
            }
        }
    }
    
    public function __get( $key )
    {
        return $this->offsetGet($key);
    }
    
    public function __set($key, $value)
    {
        $this->offsetSet($key,$value);
    }
    
    public function __isset($name)
    {
        // TODO: Implement __isset() method.
    }
    
    /**
     * 根据类名获取对象
     *
     * @param string $class_name 类名
     *
     * @return mixed|object
     */
    public function build($class_name)
    {
        if (is_string($class_name) && isset($this->instance[$class_name]))
        {
            return $this->instance[$class_name];
        }
        
        //反射
        $reflector = new \ReflectionClass($class_name);
        
        //判断是否可以实例化
        if (!$reflector->isInstantiable())
        {
            throw new \Exception("无法实例化");
        }
        
        //获取类构造函数,若无构造函数，直接可以new一下
        $constuctor = $reflector->getConstructor();
        if (is_null($constuctor))
        {
            return new $class_name;
        }
        
        //获取构造函数参数
        $params = $constuctor->getParameters();
        
        //解析构造函数的参数
        $params = $this->getDependencies($params);
        
        //创建一个类的新实例，给出的参数传递到类的构造方法
        $class = $reflector->newInstanceArgs($params);
        $this->instance[$class_name] = $class;
        return $class;
    }
    
    /**
     * 根据参数获取相关依赖
     *
     * @param array $params 参数
     *
     * @return mixed
     */
    public function getDependencies(array $params)
    {
        $dependencies = [];
        foreach($params as $param)
        {
            $tmp = $param->getClass();
            if (is_null($tmp))
            {
                $dependencies[] = $this->resolveNonClass($params);
            }
            else
            {
                $dependencies[] = $this->build($tmp->name);
            }
        }
        
        return $dependencies;
    }
    
    /**
     * 处理非class类
     */
    public function resolveNonClass(\ReflectionParameter $parameter)
    {
        if ($parameter->isDefaultValueAvailable())
        {
            return $parameter->getDefaultValue();
        }
        
        throw new \Exception("系统错误");
    }
    
    public function _autoload($path)
    {
        spl_autoload_register(function($class)use($path){
            $file = DIRECTORY_SEPARATOR.str_replace("\\",DIRECTORY_SEPARATOR,$class).'.php';
            if (is_file($file))
            {
                include($path.$file);
                return true;
            }
            
            return false;
        });
    }
}